package com.gujiangbo.redemo;

import com.github.javafaker.Faker;
import org.apache.commons.lang3.StringUtils;
import org.junit.jupiter.api.Test;
import redis.clients.jedis.Jedis;

import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.IntStream;

/**
 * Hash 类型
 */
public class RedisHashDemo {

    public Jedis jedis = JedisFactory.getJedis();

    /**
     * hset key field value 设置值
     */
    @Test
    public void testHsetKey() {
        jedis.hset("student:1", "name", "GuJiangBo");
        jedis.close();
    }

    /**
     * Hmset key map<String,String> 批量设置
     */
    @Test
    public void testHmsetKey() {
        Map<String, String> map = new HashMap<>();
        map.put("name", "GuJiangBo");
        map.put("age", "30");
        map.put("address", "JiangSu LianYunGangShi");
        jedis.hmset("student:1", map);
        jedis.close();
    }

    @Test
    public void testBatchHmSetKey() {
        Faker faker = new Faker();
        jedis.select(4);
        AtomicInteger atomicInteger = new AtomicInteger(0);
        ConcurrentMap<String, String> hashMap = new ConcurrentHashMap<>();
        IntStream.range(0, 1000).forEach(v -> {
            String id = StringUtils.leftPad(String.valueOf(atomicInteger.incrementAndGet()), 5, "0");
            hashMap.put("name", faker.name().name());
            hashMap.put("address", faker.address().fullAddress());
            hashMap.put("age", "23");
            hashMap.put("pet", faker.animal().name());
            hashMap.put("book", faker.book().title());
            jedis.hmset("student:" + id, hashMap);
        });
        jedis.close();


    }

    /**
     * hget key field 获取值
     */
    @Test
    public void testHgetKey() {
        String name = jedis.hget("student:1", "name");
        System.out.println(name);
        jedis.close();
    }

    /**
     * hdel key field [field .....] 删除key
     */
    @Test
    public void testHdelKey() {
        jedis.hdel("student:1", "name");
        jedis.close();
    }

    /**
     * Hlen 统计key中field的个数
     */
    @Test
    public void testHlen() {
        Long hlen = jedis.hlen("student:1");
        System.out.println(hlen);
        jedis.close();
    }

    /**
     * hexists key field 判断field是否存在
     */
    @Test
    public void testHexistsKey() {
        Boolean name = jedis.hexists("student:1", "name");
        Boolean age = jedis.hexists("student:1", "age");
        System.out.println(name == false ? "name:不存在" : "name:存在");
        System.out.println(age == false ? "age:不存在" : "age:存在");
        jedis.close();
    }

    /**
     * hkeys key 获取所有的key
     */
    @Test
    public void testHkeys() {
        Set<String> hkeys = jedis.hkeys("student:1");
        hkeys.stream().forEach(System.out::println);
        jedis.close();
    }

    /**
     * hvals key : 获取所有的value
     */
    @Test
    public void testHvals() {
        List<String> hvals = jedis.hvals("student:1");
        hvals.stream().forEach(System.out::println);
        jedis.close();
    }

    /**
     * hgetall key 获取所有的field-value
     */
    @Test
    public void testHgetAll() {
        Map<String, String> map = jedis.hgetAll("user:1");
        Set<Map.Entry<String, String>> entries = map.entrySet();
        entries.stream().forEach(i -> System.out.println(i.getKey() + ":" + i.getValue()));
        jedis.close();
    }

    /**
     * hincrby  key field value 作用域是field，自增value
     */
    @Test
    public void testIncrBy() {
        Long age = jedis.hincrBy("student:1", "age", 1);
        System.out.println(" age Hincrby : " + age);
        jedis.close();
    }

    /**
     * Hash 内部编码
     * 哈希类型的内部编码有两种：
     * ziplist(压缩列表)：
     * 当哈希类型元素个数小于hash-max-ziplist-entries配置(默认512),同时所有值小于hash-max-ziplist-value配置
     * (默认64字节)时，Redis会使用ziplist作为哈希的内部实现，ziplist使用更加紧凑的结构实现多个元素的连续存储，所以
     * 在节省内存方面比hashtable更加优秀。
     * hashtable(哈希表)：
     * 当哈希类型无法满足ziplist的条件时，Redis会使用hashtable作为哈希的内部实现，因为此时ziplist的读写效率会下降，
     * 而hashtable的读写时间复杂度为O(1)。
     */
    @Test
    public void testObjEncoding() {
        jedis.select(4);
        jedis.hset("student:1", "message", "this is a obj student");
        String objectEncoding = jedis.objectEncoding("student:1");
        System.out.println(objectEncoding);
        //当field 字符长度超过64 则转换为hashtable
        jedis.hset("student:2", "message",
                "this is a story ! can you speak chinese ? " +
                        "how are you ? what's you work ? are you ok ? do you know me ? how old are you ?");
        String objectEncoding1 = jedis.objectEncoding("student:2");
        System.out.println(objectEncoding1);

        Map<String, String> map = new HashMap<>();
        for (int i = 0; i < 513; i++) {
            map.put("name-" + i, "张三" + i);
        }
        jedis.hmset("student:3", map);
        // 当field 个数超过512，使用hashtable
        String objectEncoding2 = jedis.objectEncoding("student:3");
        System.out.println(objectEncoding2);
        jedis.close();

    }
}
